
#include <BScript>
#include "ScriptFormat.h"

ScriptFormat::ScriptFormat()
{

}
ScriptFormat::~ScriptFormat()
{

}

void ScriptFormat::styled(const BStyle* style)
{

}
void ScriptFormat::changed(const BString& text)
{
	_itemMap.reset();
	auto cit = text.begin();
	BString word;
	while (cit.valid())
	{
		auto begin = nextWord(word, text, cit);
		auto it = _itemMap.insert(begin.count());
		it->range.set(begin.count(), cit.count());
		if (word.beginWith('\"'))
		{
			it->color.set(128, 10, 10);
			continue;
		}
		if (word.beginWith('$') || word.beginWith('@'))
		{
			it->color.set(128, 128, 10);
			continue;
		}
		if (word == "none")
		{
			it->color.set(128, 32, 64);
			continue;
		}
		if (word == "true" || word == "false")
		{
			it->color.set(56, 100, 200);
			continue;
		}
		if (word == "self")
		{
			it->color.set(128, 32, 128);
			continue;
		}
		if (word == "begin" || word == "end")
		{
			it->color.set(64, 86, 128);
			continue;
		}
		if (word == "include")
		{
			it->color.set(123, 100, 86);
			continue;
		}
		if (checkType(word))
		{
			it->color.set(56, 128, 200);
			continue;
		}
		if (checkSyntax(word))
		{
			it->color.set(128, 32, 128);
			continue;
		}
		if (_script && _script->findClass(word))
		{
			it->color.set(36, 100, 66);
			continue;
		}
	}
}

ScriptFormat::ItemMap& ScriptFormat::itemMap()
{
	return _itemMap;
}
const ScriptFormat::ItemMap& ScriptFormat::itemMap() const
{
	return _itemMap;
}

const BFont* ScriptFormat::font(int pos) const
{
	auto it = _itemMap <= pos;
	if (it.valid())
	{
		if (pos < it->range.max())
			return it->font;
	}
	return 0;
}
const BColor* ScriptFormat::back(int pos) const
{
	return 0;
}
const BColor* ScriptFormat::color(int pos) const
{
	auto it = _itemMap <= pos;
	if (it.valid())
	{
		if (pos < it->range.max())
			return &it->color;
	}
	return 0;
}

BString::const_iterator ScriptFormat::nextWord(BString& word, const BString& text, BString::const_iterator& cit)
{
	while (cit.valid())
	{
		if (!cit->isSpace())
			break;
		cit++;
	}
	if (cit.empty())
	{
		word.reset();
		return cit;
	}
	auto begin = cit;
	if (cit == ':' && (cit + 1) == ':')
	{
		word = "::";
		cit += 2;
		return begin;
	}
	if (cit == '\"')
	{
		auto begin = cit++;
		cit = text.find('\"', cit);
		word.reset(text, begin, ++cit);
		return begin;
	}
	if (cit == '$' || cit == '@')
	{
		auto begin = cit++;
		cit = text.word(word, cit);
		word.prepend(*cit);
		return begin;
	}
	if (cit == '+' && (cit + 1) == '+')
	{
		word = "++";
		cit += 2;
		return begin;
	}
	if (cit == '-' && (cit + 1) == '-')
	{
		word = "--";
		cit += 2;
		return begin;
	}
	if (cit == '.' || cit == ',' || cit == ';' || cit == '(' || cit == ')' || cit == '[' || cit == ']' || cit == '{' || cit == '}')
	{
		word = cit;
		cit += 1;
		return begin;
	}
	if (cit == '<' || cit == '>' || cit == '!' || cit == '+' || cit == '-' || cit == '*' || cit == '/' || cit == '|')
	{
		word = cit;
		if ((cit + 1) == '=')
		{
			word << '=';
			cit += 2;
			return begin;
		}
		cit++;
		return begin;
	}
	if (cit->isDigit())
	{
		auto end = cit + 1;
		while (end.valid())
		{
			if (end == '.' && !(end + 1)->isDigit())
				break;
			if (!end->isDigit())
				break;
			++end;
		}
		word.reset(text, begin, end);
		cit = end;
		return begin;
	}
	if (cit != '_' && cit->isPunct())
	{
		auto end = cit;
		while (end.valid())
		{
			if (end->isSpace())
				break;
			if (cit == '_' || !end->isPunct())
				break;
			end++;
		}
		word.reset(text, begin, end);
		cit = end;
		return begin;
	}
	while (cit.valid())
	{
		if (cit->isSpace())
			break;
		if (cit != '_' && cit->isPunct())
			break;
		cit++;
	}
	word.reset(text, begin, cit);
	return begin;
}
bool ScriptFormat::checkType(const BString& text)
{
	if (text == "void")
		return true;
	if (text == "bool")
		return true;
	if (text == "char")
		return true;
	if (text == "byte")
		return true;
	if (text == "short")
		return true;
	if (text == "ushort")
		return true;
	if (text == "int")
		return true;
	if (text == "uint")
		return true;
	if (text == "long")
		return true;
	if (text == "ulong")
		return true;
	if (text == "float")
		return true;
	if (text == "double")
		return true;
	if (text == "string")
		return true;
	return false;
}
Syntax ScriptFormat::checkSyntax(const BString& text)
{
	if (text == "class")
		return Syntax_Class;
	if (text == "access")
		return Syntax_Access;
	if (text == "const")
		return Syntax_Const;
	if (text == "extern")
		return Syntax_Extern;
	if (text == "viratual")
		return Syntax_Virtual;
	if (text == "inline")
		return Syntax_Inline;
	if (text == "if")
		return Syntax_If;
	if (text == "else")
		return Syntax_Else;
	if (text == "elif")
		return Syntax_Else;
	if (text == "for")
		return Syntax_For;
	if (text == "while")
		return Syntax_While;
	if (text == "switch")
		return Syntax_Switch;
	if (text == "case")
		return Syntax_Case;
	if (text == "default")
		return Syntax_Default;
	if (text == "continue")
		return Syntax_Continue;
	if (text == "break")
		return Syntax_Break;
	if (text == "return")
		return Syntax_Return;
	return Syntax_None;
}

void ScriptFormat::slotAssetChanged(BObject* object, const BValue& value)
{
	BScript* script = value;
	if (_script != script)
	{
		_script = script;
	}
}
